////////////////////////////////////////////////////////////////////////////////////////////////
//
//  ResizableTitleWindow(6/11/06 Nisheet Jain) v.1.1
//     
//  Ported from  -
//  Manish Jethani's ResizableTitleWindow
//  extended by TYPE.de
//  
//  This basic design for this class has been taken from Manish Jethani's ResizableTitleWindow
//  http://manish.revise.org/archives/2005/01/09/resizable-titlewindow-in-flex/
//
// CHANGELOG:
// 2010_07_15_TIME_15:45:27 - integrating external stylesheet into one single file ResizableTitleWindow.as (easier to handle for me, no css includes inside mxml necessary) (by TYPE.de)
// 2010_07_16_TIME_15:45:27 - limiting the x and y coordinates at which the popup can occur (to not overlap with e.g. toplevel menu etc. or move out of the window, x:0,y:0 starts in the left-top corner) (by TYPE.de)
// 2010_07_20_TIME_10:45:10 - making this functions public toMinimized() toMaximized() toNormal()
//
////////////////////////////////////////////////////////////////////////////////////////////////

package sdk3.components.ResizableTitleWindow 
{
import adobe.utils.CustomActions;

import sdk3.components.ResizableTitleWindow.utilityClasses.DiagonalResizeCursor;
import sdk3.components.ResizableTitleWindow.utilityClasses.HorizontalResizeCursor;
import sdk3.components.ResizableTitleWindow.utilityClasses.RotatedDiagonalResizeCursor;
import sdk3.components.ResizableTitleWindow.utilityClasses.TaskBarManager;
import sdk3.components.ResizableTitleWindow.utilityClasses.VerticalResizeCursor;

import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;

import mx.containers.TitleWindow;
import mx.controls.Button;
import mx.controls.ToggleButtonBar;
import mx.core.Application;
import mx.core.Container;
import mx.core.EdgeMetrics;
import mx.core.FlexVersion;
import mx.core.IUIComponent;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.effects.Move;
import mx.effects.Parallel;
import mx.effects.Pause;
import mx.effects.Resize;
import mx.effects.Sequence;
import mx.effects.easing.Bounce;
import mx.effects.easing.Circular;
import mx.effects.easing.Linear;
import mx.events.CloseEvent;
import mx.managers.CursorManager;
import mx.managers.CursorManagerPriority;
import mx.managers.ISystemManager;
import mx.managers.SystemManager;
import mx.states.SetProperty;
import mx.states.State;
import mx.states.Transition;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;
import mx.styles.StyleProxy;

use namespace mx_internal;
//--------------------------------------
//  Events
//--------------------------------------

/**
*  Dispatched when the user selects the maximize button.
*/
[Event(name="maximize", type="flash.events.Event")]

/**
*  Dispatched when the user selects the maximize button.
*/
[Event(name="minimize", type="flash.events.Event")]

/**
*  Dispatched when the user restores the window
*/
[Event(name="restore", type="flash.events.Event")]

//--------------------------------------
//  Styles
//--------------------------------------

/**
*  Thickness in pixels of the area where the user can click to resize 
*  the window.
*  This area is centered at the edge of the window
*  A resize cursor appears when the mouse is over this area.
*
*  @default 6
*/

[Style(name="resizeAffordance", type="Number", format="Length", inherit="no")]

/**
 *  The maximize button default skin.
 *
 *  @default null
 */
[Style(name="maximizeButtonSkin", type="Class", inherit="no", states="up, over, down, disabled")]

/**
 *  The maximize button disabled skin.
 *
 *  @default MaximizeButtonDisabled
 */
[Style(name="maximizeButtonDisabledSkin", type="Class", inherit="no")]

/**
 *  The maximize button down skin.
 *
 *  @default MaximizeButtonDown
 */
[Style(name="maximizeButtonDownSkin", type="Class", inherit="no")]

/**
 *  The maximize button over skin.
 *
 *  @default MaximizeButtonOver
 */
[Style(name="maximizeButtonOverSkin", type="Class", inherit="no")]

/**
 *  The maximize button up skin.
 *
 *  @default MaximizeButtonUp
 */
[Style(name="maximizeButtonUpSkin", type="Class", inherit="no")]

/**
 *  The minimize button default skin.
 *
 *  @default null
 */
[Style(name="minimizeButtonSkin", type="Class", inherit="no", states="up, over, down, disabled")]

/**
 *  The minimize button disabled skin.
 *
 *  @default MinimizeButtonDisabled
 */
[Style(name="minimizeButtonDisabledSkin", type="Class", inherit="no")]

/**
 *  The minimize button down skin.
 *
 *  @default MinimizeButtonDown
 */
[Style(name="minimizeButtonDownSkin", type="Class", inherit="no")]

/**
 *  The minimize button over skin.
 *
 *  @default MinimizeButtonOver
 */
[Style(name="minimizeButtonOverSkin", type="Class", inherit="no")]

/**
 *  The minimize button up skin.
 *
 *  @default MinimizeButtonUp
 */
[Style(name="minimizeButtonUpSkin", type="Class", inherit="no")]

//--------------------------------------
//  Excluded APIs
//--------------------------------------

[Exclude(name="doubleClickEnabled", kind="property")]
[Exclude(name="showCloseButton", kind="property")]

/**
*  A sdk3.components.ResizableTitleWindow is an extension to a TitleWindow which can be 
*  resized/maximized/minimized/moved/closed. It can be used as a Container to 
*  contain other components.
*  <br>NOTE: Its behavior is not meaningful when it is put inside some other container
* like VBox, VDividedBox etc.
*
*  @mxml
*
*  <p>The <code>&lt;fc:sdk3.components.ResizableTitleWindow&gt;</code> tag inherits all the tag attributes
*  of its superclass, and adds the following tag attributes:</p>
*
*  <pre>
*  &lt;fc:sdk3.components.ResizableTitleWindow
*    <b>Properties</b>
*    closeable="true"
*    maximizable="true"
*    minimizable="true"
*    
*    <b>Styles</b>
*    resizeAffordance="6"
*    maximizeButtonDisabledSkin="<i>Expects in application</i>"
*    maximizeButtonDownSkin="<i>Expects in application</i>"
*    maximizeButtonOverSkin="<i>Expects in application</i>"
*    maximizeButtonUpSkin="<i>Expects in application</i>"
*
*    minimizeButtonDisabledSkin="<i>Expects in application</i>"
*    minimizeButtonDownSkin="<i>Expects in application</i>"
*    minimizeButtonOverSkin="<i>Expects in application</i>"
*    minimizeButtonUpSkin="<i>Expects in application</i>"
*    
*    <b>Events</b>
*    maximize="<i>No default</i>"
*    minimize="<i>No default</i>"
*    restore="<i>No default</i>"
*  /&gt;
*  </pre>
*
*  @includeExample samples/sdk3.components.ResizableTitleWindowSample/sdk3.components.ResizableTitleWindowSample.mxml
*
*/                                                                                                                                    
public class ResizableTitleWindow extends TitleWindow
{
    //--------------------------------------------------------------------------
    //
    //  image / icon assets
    //
    //--------------------------------------------------------------------------
	[Embed(source="/images/close.png")]
	private var closeButtonSkin:Class;
/*	[Embed(source="/images/close.png")]
	private var closeButtonDisabledSkin:Class;*/
	[Embed(source="images/close.png")]
	private var closeButtonDownSkin:Class;
	[Embed(source="images/close.png")]
	private var closeButtonOverSkin:Class;
	[Embed(source="/images/close.png")]
	private var closeButtonUpSkin:Class;
	
	[Embed(source="/images/min.png")]
	private var minimizeButtonSkin:Class;
/*	[Embed(source="/images/min.png")]
	private var minimizeButtonDisabledSkin:Class;*/
	[Embed(source="/images/min.png")]
	private var minimizeButtonDownSkin:Class;
	[Embed(source="/images/min.png")]
	private var minimizeButtonOverSkin:Class;
	[Embed(source="/images/min.png")]
	private var minimizeButtonUpSkin:Class;
	
	[Embed(source="/images/max.png")]
	private var maximizeButtonSkin:Class;
/*	[Embed(source="/images/max.png")]
	private var maximizeButtonDisabledSkin:Class;*/
	[Embed(source="/images/max.png")]
	private var maximizeButtonDownSkin:Class;
	[Embed(source="/images/max.png")]
	private var maximizeButtonOverSkin:Class;
	[Embed(source="/images/max.png")]
	private var maximizeButtonUpSkin:Class;

	[Embed(source="/images/restore.png")]
	private var restoreButtonSkin:Class;
	[Embed(source="/images/restore.png")]
	private var restoreButtonDownSkin:Class;
	[Embed(source="/images/restore.png")]
	private var restoreButtonOverSkin:Class;
	[Embed(source="/images/restore.png")]
	private var restoreButtonUpSkin:Class;
/*	[Embed(source="/images/restore.png")]
	private var restoreButtonDisabledSkin:Class;*/

    //--------------------------------------------------------------------------
    //
    //  Class Constants
    //
    //--------------------------------------------------------------------------

    // Constants for window edges (see `handleEdge`)                                                                                                                    
    static private var EDGE_NONE : Number = 0;

    static private var EDGE_BOTTOM : Number = 1;
    static private var EDGE_RIGHT : Number = 2;
    static private var EDGE_LEFT : Number = 3;
    static private var EDGE_TOP : Number = 4;

    static private var EDGE_CORNER : Number = 5;
    static private var EDGE_LEFT_BOTTOM : Number = 6;
    static private var EDGE_LEFT_TOP : Number = 7;
    static private var EDGE_RIGHT_TOP : Number = 8;

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     */
    public function ResizableTitleWindow()
    {
        super();

        isPopUp = true;
        doubleClickEnabled = true;

        setStyle("headerHeight", 21); 
        setStyle("cornerRadius", 4);
        setStyle("borderAlpha", 1);
        setStyle("dropShadowEnabled", true);
        setStyle("resizeAffordance",6);

        addEventListener(Event.CLOSE,closeHandler);
        addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler,true);
        addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler,false);
        addEventListener(MouseEvent.MOUSE_MOVE, systemManager_mouseMoveHandler);
        addEventListener(MouseEvent.CLICK, bringToFront,false);
        addEventListener(MouseEvent.ROLL_OUT, systemManager_mouseMoveHandler);
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    //--------------------------------------------------------------------------
    //  Various States of the window
    //--------------------------------------------------------------------------
    /**
     *  @private
     */
    private var stateNormal:State;

    /**
     *  @private
     */
    private var stateMin:State;

    /**
     *  @private
     */
    private var stateMax:State;

    /**
     *  @private
     */
    private var statePseudoMax:State;

    //--------------------------------------------------------------------------
    //  Resize Cursor Classes
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  CursorSymbol used to display Vertical Resize Cursor
     */
    private var vCursorSymbol:Class = VerticalResizeCursor;
    /**
     *  @private
     *  CursorSymbol used to display Horizontal Resize Cursor
     */
    private var hCursorSymbol:Class = HorizontalResizeCursor;
    /**
     *  @private
     *  CursorSymbol used to display Diagonal Resize Cursor
     */
    private var dCursorSymbol:Class = DiagonalResizeCursor;

    /**
     *  @private
     *  CursorSymbol used to display Rotated Diagonal Resize Cursor
     */
    private var rdCursorSymbol:Class = RotatedDiagonalResizeCursor;
    /**
     *  @private
     */
    private var fullTitle:String = "";

    /**
     *  @private
     */
    private var isHandleDragging : Boolean = false;

    /**
     *  @private
     */
    private var handleEdge : Number = EDGE_NONE;

    /**
     *  @private
     */
    private var cursorId:Number=0;

    /**
     *  @private
     */
    private var lastEdge:Number=0;

    /**
     *  @private
     *  A reference to the minimizeButton
     */
    private var minimizeButton:Button;

    /**
     *  @private
     *  A reference to the maximizeButton
     */
    private var maximizeButton:Button;

    /**
     *  @private
     */
    private var origPosition:Point;

    /**
     *  @private
     */
    private var origDimensions:Point;

    /**
     *  @private
     */
    private var origPerDimensions:Point;

    /**
     *  @private
     */
    private var origDimensionsCached:Boolean = false;

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    [Inspectable(category="General")]

    /**
     *  Whether to display a Maximize button in the sdk3.components.ResizableTitleWindow container.
     *  Set it to <code>false</code> if you don't want to display the Maximize button.
     *  @default true
     *
     */
    private var _maximizable:Boolean=true;
    public function get maximizable():Boolean
    {
        return _maximizable;
    }
    public function set maximizable(value:Boolean):void
    {
        _maximizable = value;
        invalidateDisplayList();
    }

    [Inspectable(category="General")]

    /**
     *  Whether to display a Minimize button in the sdk3.components.ResizableTitleWindow container.
     *  Set it to <code>false</code> if you don't want to display the Minimize button.
     *  @default true
     *
     */
    private var _minimizable:Boolean=true;
    public function get minimizable():Boolean
    {
        return _minimizable;
    }
    public function set minimizable(value:Boolean):void
    {
        _minimizable = value;
        invalidateDisplayList();
    }

    [Inspectable(category="General")]
    /**
     *  Whether to display a Close button in the sdk3.components.ResizableTitleWindow container.
     *  Set it to <code>false</code> if you don't want to display the Close button.
     *  @default true
     *
     */
    private var _closeable:Boolean=true;
    public function get closeable():Boolean
    {
        return _closeable;
    }
    public function set closeable(value:Boolean):void
    {
        _closeable = value;
        invalidateDisplayList();
    }

    //----------------------------------
    //  _minimizeButtonStyleFilters
    //----------------------------------


    private static var _minimizeButtonStyleFilters:Object = 
    {
        "minimizeButtonUpSkin" : "minimizeButtonUpSkin", 
        "minimizeButtonOverSkin" : "minimizeButtonOverSkin",
        "minimizeButtonDownSkin" : "minimizeButtonDownSkin",
        "minimizeButtonDisabledSkin" : "minimizeButtonDisabledSkin",
        "minimizeButtonSkin" : "minimizeButtonSkin",
        "repeatDelay" : "repeatDelay",
        "repeatInterval" : "repeatInterval"
    };

    /**
     *  The set of styles to pass from the Panel to the minimize button.
     *  @see mx.styles.StyleProxy
     *  @review
     */
    protected function get minimizeButtonStyleFilters():Object
    {
        return _minimizeButtonStyleFilters;
    }
    //----------------------------------
    //  _maximizeButtonStyleFilters
    //----------------------------------


    private static var _maximizeButtonStyleFilters:Object = 
    {
        "maximizeButtonUpSkin" : "maximizeButtonUpSkin", 
        "maximizeButtonOverSkin" : "maximizeButtonOverSkin",
        "maximizeButtonDownSkin" : "maximizeButtonDownSkin",
        "maximizeButtonDisabledSkin" : "maximizeButtonDisabledSkin",
        "maximizeButtonSkin" : "maximizeButtonSkin",
        "repeatDelay" : "repeatDelay",
        "repeatInterval" : "repeatInterval"
    };

    /**
     *  The set of styles to pass from the Panel to the maximize button.
     *  @see mx.styles.StyleProxy
     *  @review
     */
    protected function get maximizeButtonStyleFilters():Object
    {
        return _maximizeButtonStyleFilters;
    }
	

	/**
	 *  @private
	 */
	private var _nextWindowY:Number = 0;
	public function get nextWindowY():Number
	{
		return _nextWindowY;
	}
	public function set nextWindowY(value:Number):void
	{
		this._nextWindowY = value;
	}
	
	/**
	 *  @private
	 */
	private var _nextWindowX:Number = 0;
	public function get nextWindowX():Number
	{
		return _nextWindowX;
	}
	public function set nextWindowX(value:Number):void
	{
		this._nextWindowX = value;
	}

	/** if there is a limit to what Y coordinate is the maYimum the popup should be at during creation and drag + drop
	 * valid values are any numeric values + maximum
	 * **/
	public var minimumY:String = "unlimited";
	
	/** if there is a limit to what Y coordinate is the maYimum the popup should be at during creation and drag + drop
	 * valid values are any numeric values + maximum
	 * **/
	public var maximumY:String = "unlimited";
	
	//--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

	/**
	 *  Number that specifies the component's vertical position,
	 *  in pixels, within its parent container.
	 *
	 *  <p>Setting this property directly or calling <code>move()</code>
	 *  will have no effect -- or only a temporary effect -- if the
	 *  component is parented by a layout container such as HBox, Grid,
	 *  or Form, because the layout calculations of those containers
	 *  set the <code>x</code> position to the results of the calculation.
	 *  However, the <code>x</code> property must almost always be set
	 *  when the parent is a Canvas or other absolute-positioning
	 *  container because the default value is 0.</p>
	 *
	 *  @default 0
	 */
	override public function get y():Number
	{
		return super.y;
	}
	
	/**
	 *  @private
	 */
	override public function set y(value:Number):void
	{
		if(maximumY != "unlimited")
		{
			if(super.y > parseFloat(maximumY))
			{
				super.y = parseFloat(maximumY);
			}
		}
		if(maximumY != "unlimited")
		{
			if(super.y < parseFloat(minimumY))
			{
				super.y = parseFloat(minimumY);
			}
		}

		if (super.y == value)
			return;
		
		super.y = value;
		
		invalidateProperties();
		
		dispatchEvent(new Event("yChanged"));
	}

	/** if there is a limit to what X coordinate is the maximum the popup should be at during creation and drag + drop
	 * valid values are any numeric values + unlimited
	 * **/
	public var minimumX:String = "unlimited";
	/** if there is a limit to what X coordinate is the maximum the popup should be at during creation and drag + drop
	 * valid values are any numeric values + unlimited
	 * **/
	public var maximumX:String = "unlimited";

	/**
	 *  Number that specifies the component's horizontal position,
	 *  in pixels, within its parent container.
	 *
	 *  <p>Setting this property directly or calling <code>move()</code>
	 *  will have no effect -- or only a temporary effect -- if the
	 *  component is parented by a layout container such as HBox, Grid,
	 *  or Form, because the layout calculations of those containers
	 *  set the <code>x</code> position to the results of the calculation.
	 *  However, the <code>x</code> property must almost always be set
	 *  when the parent is a Canvas or other absolute-positioning
	 *  container because the default value is 0.</p>
	 *
	 *  @default 0
	 */
	override public function get x():Number
	{
		return super.x;
	}
	
	/**
	 *  @private
	 */
	override public function set x(value:Number):void
	{
		if(maximumX != "unlimited")
		{
			if(super.x > parseFloat(maximumX))
			{
				super.x = parseFloat(maximumX);
			}
		}
		if(minimumX != "unlimited")
		{
			if(super.x < parseFloat(minimumX))
			{
				super.x = parseFloat(minimumX);
			}
		}

		if (super.x == value)
			return;
		
		super.x = value;
		
		invalidateProperties();
		
		dispatchEvent(new Event("xChanged"));
	}
	
    /**
     *  @private
     *  Create child objects.
     */
    override protected function createChildren():void
    {
        super.createChildren();

        var sm:ISystemManager;
        if ( parent is ISystemManager)
         	sm = ISystemManager(parent);
        else if(parent is IUIComponent)
        {
        	sm = IUIComponent(parent).systemManager;
            //Register this window to the TaskbarManager
            TaskBarManager.registerWindow(this);
        }
        if(sm)
	        sm.addEventListener(Event.RESIZE, resizeHandler, false, -5);

        //Apply the Resize and Move effects to support Animation
        setEffects();

        fullTitle = title;

        storeOrigDimensions();

        if (titleBar)
        {
            titleBar.height = getHeaderHeight();
            titleBar.addEventListener(MouseEvent.DOUBLE_CLICK, titleBar_mouseClickHandler);

            if(closeable)
                showCloseButton = true;

            if (!minimizeButton)
            {
                minimizeButton = new Button();

                minimizeButton.styleName = new StyleProxy(this, minimizeButtonStyleFilters);

				// SET STYLE programatically
				minimizeButton.setStyle("icon",minimizeButtonSkin);
				minimizeButton.setStyle("upSkin",minimizeButtonUpSkin);
				minimizeButton.setStyle("overSkin",minimizeButtonOverSkin);
				minimizeButton.setStyle("downSkin",minimizeButtonDownSkin);
				// minimizeButton.setStyle("disabledSkin",minimizeButtonDownSkin);
				minimizeButton.setStyle("overSkin",minimizeButtonOverSkin);
				minimizeButton.setStyle("downSkin",minimizeButtonDownSkin);
				
                minimizeButton.upSkinName = "minimizeButtonUpSkin";
                minimizeButton.overSkinName = "minimizeButtonOverSkin";
                minimizeButton.downSkinName = "minimizeButtonDownSkin";
                minimizeButton.disabledSkinName = "minimizeButtonDisabledSkin";
                minimizeButton.skinName = "minimizeButtonSkin";

                minimizeButton.explicitWidth = 16;
                minimizeButton.explicitHeight = 16;

                minimizeButton.visible = false;
                minimizeButton.enabled = enabled && parent is IUIComponent;

                minimizeButton.focusEnabled = false;

                minimizeButton.addEventListener(MouseEvent.CLICK, titleBar_mouseClickHandler);
                // Add the minimize button on top of the title/status.
                titleBar.addChild(minimizeButton);
                minimizeButton.owner = this;

            }
            if (!maximizeButton)
            {
                maximizeButton = new Button();
                maximizeButton.styleName = new StyleProxy(this, maximizeButtonStyleFilters);

				// SET STYLE programatically
                maximizeButton.setStyle("icon",maximizeButtonSkin);
                maximizeButton.setStyle("upSkin",maximizeButtonUpSkin);
                maximizeButton.setStyle("overSkin",maximizeButtonOverSkin);
                maximizeButton.setStyle("downSkin",maximizeButtonDownSkin);
                // maximizeButton.setStyle("disabledSkin",maximizeButtonDownSkin);
                maximizeButton.setStyle("overSkin",maximizeButtonOverSkin);
                maximizeButton.setStyle("downSkin",maximizeButtonDownSkin);
				
                maximizeButton.upSkinName = "maximizeButtonUpSkin";
                maximizeButton.overSkinName = "maximizeButtonOverSkin";
                maximizeButton.downSkinName = "maximizeButtonDownSkin";
                maximizeButton.disabledSkinName = "maximizeButtonDisabledSkin";
                maximizeButton.skinName = "maximizeButtonSkin";

                maximizeButton.explicitWidth = 16;
                maximizeButton.explicitHeight = 16;

                maximizeButton.visible = false;
                maximizeButton.enabled = enabled;
                maximizeButton.focusEnabled = false;

                maximizeButton.addEventListener(MouseEvent.CLICK, titleBar_mouseClickHandler);
                // Add the maximize button on top of the title/status.
                titleBar.addChild(maximizeButton);
                maximizeButton.owner = this;

            }
        }
    }

    /**
     *  @private
     */
    override protected function layoutChrome(unscaledWidth:Number,
                                             unscaledHeight:Number):void
    {
        super.layoutChrome(unscaledWidth,unscaledHeight);

        var maxButtonHeight:uint = 0;
        var headerHeight:Number = getHeaderHeight();

        var vm:EdgeMetrics = FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0 ? borderMetrics : EdgeMetrics.EMPTY;
        var gap:uint = vm.right;
        var x:uint=0;
        gap = 3;
        maximizeButton.visible = maximizable;
        minimizeButton.visible = minimizable;

        if(maximizable)
            maxButtonHeight = Math.max(maxButtonHeight, maximizeButton.getExplicitOrMeasuredHeight());
        if(minimizable)
            maxButtonHeight = Math.max(maxButtonHeight, minimizeButton.getExplicitOrMeasuredHeight());
        if(closeable)
            maxButtonHeight = Math.max(maxButtonHeight, closeButton.getExplicitOrMeasuredHeight());

        //Find out the y for all the buttons
        var y:uint = Math.round(( headerHeight - maxButtonHeight ) / 2 ); 
        x = unscaledWidth - vm.right;

        var w:Number;
        var h:Number;

        if(closeable)
        {
            w = closeButton.getExplicitOrMeasuredWidth();
            h = closeButton.getExplicitOrMeasuredHeight();
            closeButton.setActualSize(w, h);

            x -=  (w + gap);
            closeButton.move(x,y);
        }

        if(maximizable)
        {
            w = maximizeButton.getExplicitOrMeasuredWidth();
            h = maximizeButton.getExplicitOrMeasuredHeight();
            maximizeButton.setActualSize(w, h);
            x -= (w+gap);
            maximizeButton.move( x, y );
        }

        if(minimizable)
        {
            w = minimizeButton.getExplicitOrMeasuredWidth();
            h = minimizeButton.getExplicitOrMeasuredHeight();

            minimizeButton.setActualSize(w, h);

            x -= (w+ gap);
            minimizeButton.move(x, y);
        }

        //Copied from mx.containers.Panel
        var leftOffset:Number = 10;
        var rightOffset:Number = unscaledWidth - vm.right - x;;
        var offset:Number;
        
            // Set the position of the title icon.
        if (titleIconObject)
        {
            h = titleIconObject.height;
            offset = (headerHeight - h) / 2;
            titleIconObject.move(leftOffset, offset);
            leftOffset += titleIconObject.width + 4;
        }

            // Set the position of the title text.  
        h = titleTextField.textHeight;
        offset = (headerHeight - h) / 2;
        
        var borderWidth:Number = vm.left + vm.right;            
        titleTextField.move(leftOffset, offset - 1);
        titleTextField.setActualSize(Math.max(0,
                                   unscaledWidth - leftOffset -
                                              rightOffset - borderWidth),
                                     h + UITextField.TEXT_HEIGHT_PADDING);
        
        // Set the position of the status text.
        h = statusTextField.textHeight;
        offset = (headerHeight - h) / 2;
        var statusX:Number = unscaledWidth - rightOffset - 4 -
            borderWidth - statusTextField.textWidth;
        if (_showCloseButton)
            statusX -= (closeButton.getExplicitOrMeasuredWidth() + 4);
        statusTextField.move(statusX, offset - 1);
        statusTextField.setActualSize(statusTextField.textWidth + 8,
                                      statusTextField.textHeight + UITextField.TEXT_HEIGHT_PADDING);
        
        // Make sure the status text isn't too long.
        // We do simple clipping here.
        var minX:Number = titleTextField.x + titleTextField.textWidth + 8;
        if (statusTextField.x < minX)
        {
            // Show as much as we can.
            statusTextField.width = Math.max(statusTextField.width -
                                             (minX - statusTextField.x), 0);
            statusTextField.x = minX;
        }
    }

    /**
     *  @private
     */
    override protected function measure():void
    {
        super.measure();

        var textSize:Rectangle = measureHeaderText();
        var textWidth:Number = textSize.width;
        var textHeight:Number = textSize.height;

        var bm:EdgeMetrics = borderMetrics;
        textWidth += bm.left + bm.right;    

        var offset:Number = 5;
        textWidth += offset * 2;

        measuredMinWidth = Math.max(textWidth, measuredMinWidth);
        measuredWidth = Math.max(textWidth, measuredWidth);

        if(minimizable)
        {
            measuredWidth += minimizeButton.getExplicitOrMeasuredWidth()+6;
            measuredMinWidth += minimizeButton.getExplicitOrMeasuredWidth()+6;
            measuredHeight = Math.max(measuredHeight, minimizeButton.getExplicitOrMeasuredHeight());
        }
        if(maximizable)
        {
            measuredWidth += maximizeButton.getExplicitOrMeasuredWidth()+6;
            measuredMinWidth += maximizeButton.getExplicitOrMeasuredWidth()+6;
            measuredHeight = Math.max(measuredHeight, maximizeButton.getExplicitOrMeasuredHeight());
        }
    }

    private function measureHeaderText():Rectangle
    {
        var textWidth:Number = 20;
        var textHeight:Number = 14;

        if (titleTextField && titleTextField.text)
        {
            titleTextField.validateNow();
            textWidth = titleTextField.textWidth;
            textHeight = titleTextField.textHeight;
        }

        if (statusTextField)
        {
            statusTextField.validateNow();
            textWidth = Math.max(textWidth, statusTextField.textWidth);
            textHeight = Math.max(textHeight, statusTextField.textHeight);
        }

        return new Rectangle(0, 0, Math.round(textWidth), Math.round(textHeight));
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number,
                                                  unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if(!origDimensionsCached)
        {
            storeOrigDimensions();
            origDimensionsCached = true;
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  Set the state for the sdk3.components.ResizableTitleWindow. The possible values are:
     *  "maximized", "minimized", "normal"
     *
     */
    protected function setState(state:String):void
    {
        if(state == "minimized" && currentState != "minimized" && parent is IUIComponent)
            toMinimized();
        else if(state == "maximized" && currentState != "maximized")
            toMaximized();
        else if(state == "normal" && currentState != "normal")
            toNormal();

    }

    //--------------------------------------------------------------------------
    //
    //  Event Handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  To keep the window with focus at the TOP.
     */
    private function bringToFront(event:MouseEvent):void
    {
        if(event.currentTarget == this && parent)
            parent.setChildIndex(this,parent.numChildren-1);
    }

    /**
     *  @public
     */
    public function closeHandler(event:Event = null):void
    {
        if(this.parent)
            this.parent.removeChild(this);
        event.stopPropagation();
    }

    /**
     *  @private
     */
    private function mouseDownHandler(event:MouseEvent):void
    {
        systemManager.addEventListener(
            MouseEvent.MOUSE_MOVE, systemManager_mouseMoveHandler, true);

        systemManager.addEventListener(
            MouseEvent.MOUSE_UP, systemManager_mouseUpHandler, true);

        systemManager_mouseDownHandler(event);
    }

    /**
     *  @private
     */
    private function systemManager_mouseDownHandler(event:MouseEvent):void
    {
        if(handleEdge != EDGE_NONE)
        {
            isHandleDragging = true;
            event.stopPropagation();
        }
    }

    /**
     *  @private
     */
    private function systemManager_mouseMoveHandler(event:MouseEvent):void
    {
        if(this.parent!=null)
        {
            if(isHandleDragging)
                resize(event);
            else
                setResizeCursor(event);
        }
    }

    /**
     *  @private
     */
    private function systemManager_mouseUpHandler(event:MouseEvent):void
    {
        systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, systemManager_mouseMoveHandler, true);
        systemManager.removeEventListener(MouseEvent.MOUSE_UP, systemManager_mouseUpHandler, true);
        systemManager.removeEventListener(MouseEvent.MOUSE_DOWN, systemManager_mouseDownHandler, true);
        isHandleDragging = false;
    }

    /**
     *  @private
     */
    private function titleBar_mouseClickHandler(event:MouseEvent):void
    {
        if(this.parent!=null && event is MouseEvent)
        {
            if(event.type == MouseEvent.DOUBLE_CLICK && event.currentTarget == titleBar)
            {
                if(currentState == "normal")
                    toMaximized();
                else if(currentState == "minimized" || currentState == "maximized")
                    toNormal();
            }
            else if(event.type == MouseEvent.CLICK)
            {
                //State Automata between minimized/maximized/normal states
                if(event.target == minimizeButton)
                {
                    if(currentState == "minimized")
                        toNormal();
                    else if(currentState == "maximized" || currentState == "normal")
                        if(parent is IUIComponent)
                            toMinimized();
                }
                else if(event.target == maximizeButton)
                {
                    if(currentState == "maximized")
                        toNormal();
                    else if(currentState == "minimized" || currentState == "normal")
                        toMaximized();

                }
                event.preventDefault();
            } 
        }
    }

    /**
     *  @private
     */
    private function isCursorOnEdge(event:MouseEvent):void
    {
        var point:Point, eventPoint:Point;
        var x1:Number, x2:Number, y1:Number, y2:Number;

        var tolerance:Number = getStyle("resizeAffordance") as Number;

        point = new Point(x,y);
        point = this.parent.localToGlobal(point);
        eventPoint = new Point(event.stageX, event.stageY);

        //Distance from right edge
        x1 = eventPoint.x - this.width - point.x;
        //Distance from left edge
        x2 = eventPoint.x-point.x;
        //Distance from bottom edge       
        y1 = eventPoint.y - this.height-point.y;
        //Distance from top edge
        y2 = eventPoint.y - point.y;

        if (event.type == "rollOut")
        {
            handleEdge = EDGE_NONE;
        }
        else
        {
            handleEdge = EDGE_NONE;
            if(handleEdge == EDGE_NONE)
            {
                if(Math.abs(x1) < tolerance && Math.abs(y1) < tolerance)
                    handleEdge = EDGE_CORNER;
                else if(Math.abs(x1) < tolerance && Math.abs(y2) < tolerance)
                    handleEdge = EDGE_RIGHT_TOP;
                else if(Math.abs(x2) < tolerance && Math.abs(y2) < tolerance)
                    handleEdge = EDGE_LEFT_TOP;
                else if(Math.abs(x2) < tolerance && Math.abs(y1) < tolerance)
                    handleEdge = EDGE_LEFT_BOTTOM;
                else if(Math.abs(y1) < tolerance)
                    handleEdge = EDGE_BOTTOM;
                else if(Math.abs(x1) < tolerance)
                    handleEdge = EDGE_RIGHT;
                else if(Math.abs(x2) < tolerance)
                    handleEdge = EDGE_LEFT;
                else if(Math.abs(y2) < tolerance)
                    handleEdge = EDGE_TOP;
            }
        }
    }

    /**
     *  @private
     */
    private function populateProperty(target:DisplayObject, name:String, value:Number):SetProperty
    {
        var sp:SetProperty = new SetProperty();
        sp.target = target;
        sp.name = name;
        sp.value = value;
        return sp;
    }


    /**
     *  @private
     */
    private function pushStateProperties(state:State, _x:int, _y:int,
                                         _width:uint, _height:uint,
                                         perWidth:uint, perHeight:uint):void
    {
        var sp:SetProperty;

        sp = populateProperty(this, "x", _x);
        state.overrides.push(sp);

        sp = populateProperty(this, "y", _y);
        state.overrides.push(sp);    	

        if(perWidth)
            sp = populateProperty(this, "percentWidth", perWidth);
        else
            sp = populateProperty(this, "width", _width);
        state.overrides.push(sp);    	

        if(perHeight)
            sp = populateProperty(this, "percentHeight", perHeight);
        else	    
            sp = populateProperty(this, "height", _height);

        state.overrides.push(sp);    	

        setCurrentState(state.name);
    }

    /**
     *  @private
     */
    private function resize(event:MouseEvent):void
    {
        var newWidth:Number, newHeight:Number;
        var point:Point = new Point(x,y);
        point = this.parent.localToGlobal(point);

        var eventPoint:Point = new Point(event.stageX, event.stageY);

        if (handleEdge == EDGE_BOTTOM)
            setSize(point.x,point.y,width, eventPoint.y-point.y);
        else if(handleEdge == EDGE_RIGHT)
            setSize(point.x,point.y,eventPoint.x-point.x, height);
        else if(handleEdge == EDGE_LEFT)
            setSize(eventPoint.x,point.y, point.x-eventPoint.x+width, height);
        else if(handleEdge == EDGE_TOP)
            setSize(point.x,eventPoint.y, width, point.y-eventPoint.y+height);
        else if(handleEdge == EDGE_CORNER)
            setSize(point.x,point.y,eventPoint.x-point.x, eventPoint.y-point.y);
        else if(handleEdge == EDGE_LEFT_TOP)
            setSize(eventPoint.x, eventPoint.y, point.x-eventPoint.x+width, point.y-eventPoint.y+height);
        else if(handleEdge == EDGE_LEFT_BOTTOM)
            setSize(eventPoint.x, point.y, point.x-eventPoint.x+width, eventPoint.y - point.y);
        else if(handleEdge == EDGE_RIGHT_TOP)
            setSize(point.x, eventPoint.y, eventPoint.x - point.x, point.y-eventPoint.y+height);
    }

    /**
     *  @private
     */
    private function resizeHandler(event:Event):void
    {
        if(currentState == "maximized")
        {
            toPseudoMax();
            toMaximized();
        }
    }

    /**
     *  @private
     */
    private function setEffects():void
    {
        stateNormal = new State();
        stateNormal.name = "normal";
        states.push(stateNormal);

        stateMin = new State();
        stateMin.name = "minimized";
        states.push(stateMin);

        stateMax = new State();
        stateMax.name = "maximized";
        states.push(stateMax);

        statePseudoMax = new State();
        statePseudoMax.name = "pseudoMax";
        states.push(statePseudoMax);

        var move:Move = new Move();
        move.duration = 200;
        move.easingFunction = Bounce.easeInOut;

        var resize:Resize = new Resize();
        resize.duration = 200;
        resize.easingFunction = Linear.easeIn;

        var parallel:Parallel = new Parallel();
        parallel.children.push(move);
        parallel.children.push(resize);
        parallel.target = this;

        var seq:Sequence = new Sequence();
        seq.children.push(parallel);

        var transition:Transition = new Transition();
        transition.effect = seq;
        transition.fromState = "*";
        transition.toState = "*";

        transitions.push(transition);
        currentState = "normal";
    }

    /**
     *  @private
     */
    private function setResizeCursor(event:MouseEvent):void
    {
        isCursorOnEdge(event);
        if (handleEdge != EDGE_NONE && currentState == "normal")
        {
            if(cursorId==0 || lastEdge != handleEdge)
            {
                if(lastEdge != handleEdge)
                {
                    CursorManager.removeCursor(cursorId);
                    cursorId=0;
                }
                if(handleEdge == EDGE_CORNER || handleEdge == EDGE_LEFT_TOP)
                    cursorId=CursorManager.setCursor(dCursorSymbol, CursorManagerPriority.HIGH);
                else if(handleEdge == EDGE_LEFT_BOTTOM || handleEdge == EDGE_RIGHT_TOP)
                    cursorId=CursorManager.setCursor(rdCursorSymbol, CursorManagerPriority.HIGH);
                else if(handleEdge == EDGE_RIGHT || handleEdge == EDGE_LEFT)
                    cursorId=CursorManager.setCursor(hCursorSymbol, CursorManagerPriority.HIGH);
                else if(handleEdge == EDGE_BOTTOM || handleEdge == EDGE_TOP)
				{
					/** kept throwing an annoying error:
						RangeError: Error #2006: The supplied index is out of bounds.
							at flash.display::DisplayObjectContainer/addChildAt()
							at mx.managers::SystemManager/http://www.adobe.com/2006/flex/mx/internal::rawChildren_addChildAt()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:2179]
							at mx.managers::SystemChildrenList/addChild()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\SystemChildrenList.as:111]
							at mx.managers::SystemManager/addChildToSandboxRoot()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:4561]
							at mx.managers::CursorManagerImpl/showCurrentCursor()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\CursorManagerImpl.as:574]
							at mx.managers::CursorManagerImpl/setCursor()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\CursorManagerImpl.as:427]
							at mx.managers::CursorManager$/setCursor()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\managers\CursorManager.as:266]
							at sdk3.components.ResizableTitleWindow::ResizableTitleWindow/setResizeCursor()[/Volumes/DATA/FLEX/PROJECTS/c_TYPE_SDK3_EXTENDED/src/components/sdk3/ResizableTitleWindow/ResizableTitleWindow.as:1325]
							at sdk3.components.ResizableTitleWindow::ResizableTitleWindow/systemManager_mouseMoveHandler()[/Volumes/DATA/FLEX/PROJECTS/c_TYPE_SDK3_EXTENDED/src/components/sdk3/ResizableTitleWindow/ResizableTitleWindow.as:1067]
							 * **/
					try
					{
	                    cursorId=CursorManager.setCursor(vCursorSymbol, CursorManagerPriority.HIGH);
					}
					catch(e: Error)
					{
						trace(e);
					}
				}

                lastEdge = handleEdge;
            }
        }
        else
        {
            if(cursorId!=0)
            {
                CursorManager.removeCursor(cursorId);
                cursorId=0;
                lastEdge = EDGE_NONE;
            }
        }
    }



    /**
     *  @private
     */
    private function setSize(newX:Number,newY:Number,newWidth:Number, newHeight:Number):void
    {
        if(newWidth > 100 && newHeight >35)
        {
            var point:Point = new Point(newX, newY);
            point = this.parent.globalToLocal(point);
            x = point.x;
            y = point.y;

            width = newWidth;
            height = newHeight;

            storeOrigDimensions();
        }
    }

    /**
     *  @private
     */
    private function storeOrigDimensions():void
    {
        origPosition = new Point();
        origDimensions = new Point();
        origPerDimensions = new Point();

        //In case this is a Pop up, special care is needed
        if (parent is ISystemManager)
        {
            origPosition.x = parent.x;
            origPosition.y = parent.y;
            origPosition = this.localToGlobal(origPosition);
        }
        else
        {
            origPosition.x = x;
            origPosition.y = y;
        }

        if(percentWidth)
            origPerDimensions.x = percentWidth;
        else
            origDimensions.x = getExplicitOrMeasuredWidth() ;

        if(percentHeight)
            origPerDimensions.y = percentHeight;
        else
            origDimensions.y = getExplicitOrMeasuredHeight();
    }

    /**
     *  @private
     */
    public function toMinimized():void
    {
        TaskBarManager.addToTaskBar(this);
        
        pushStateProperties(stateMin, x, y,
                            200, titleBar.height, 
                            NaN, NaN);
        
        minimizeButton.setStyle("upSkin", getStyle("restoreButtonUpSkin"));
        minimizeButton.setStyle("disabledSkin", getStyle("restoreButtonDisabledSkin"));
        minimizeButton.setStyle("downSkin", getStyle("restoreButtonDownSkin"));
        minimizeButton.setStyle("OverSkin", getStyle("restoreButtonOverSkin"));
        
        maximizeButton.setStyle("upSkin", getStyle("maximizeButtonUpSkin"));
        maximizeButton.setStyle("disabledSkin", getStyle("maximizeButtonDisabledSkin"));
        maximizeButton.setStyle("downSkin", getStyle("maximizeButtonDownSkin"));
        maximizeButton.setStyle("OverSkin", getStyle("maximizeButtonOverSkin"));
        
        dispatchEvent(new Event("minimize"));
    }

    /**
     *  @private
     */
    public function toMaximized():void
    {
        var point:Point = new Point(0,0);
        var maxWidth:uint, maxHeight:uint;

        if(currentState == "minimized")
            TaskBarManager.removeFromTaskBar(this);

        var vm:EdgeMetrics = new EdgeMetrics(0,0,6,8+(TaskBarManager.visible ? 27:0));

        if(parent is Application)
        {
            const s:Rectangle = ISystemManager(root).screen;
            maxWidth = s.width+4;
            maxHeight = s.height+4;
        }
        else 
        {
            maxWidth = parent.width+4;
            maxHeight = parent.height+4;

            point = parent.localToGlobal(point);
            point = parent.globalToLocal(point);
        }

        pushStateProperties(stateMax, point.x, point.y,
                            maxWidth-vm.right, maxHeight-vm.bottom,
                            NaN, NaN);

        maximizeButton.setStyle("upSkin", getStyle("restoreButtonUpSkin"));
        maximizeButton.setStyle("disabledSkin", getStyle("restoreButtonDisabledSkin"));
        maximizeButton.setStyle("downSkin", getStyle("restoreButtonDownSkin"));
        maximizeButton.setStyle("OverSkin", getStyle("restoreButtonOverSkin"));

        minimizeButton.setStyle("upSkin", getStyle("minimizeButtonUpSkin"));
        minimizeButton.setStyle("disabledSkin", getStyle("minimizeButtonDisabledSkin"));
        minimizeButton.setStyle("downSkin", getStyle("minimizeButtonDownSkin"));
        minimizeButton.setStyle("OverSkin", getStyle("minimizeButtonOverSkin"));

        dispatchEvent(new Event("maximize"));
    }

    /**
     *  @private
     */
    public function toNormal():void
    {
        if(currentState == "minimized")
            TaskBarManager.removeFromTaskBar(this);
        
        pushStateProperties(stateNormal, origPosition.x, origPosition.y,
                            origDimensions.x, origDimensions.y,
                            origPerDimensions.x, origPerDimensions.y);

        maximizeButton.setStyle("upSkin", getStyle("maximizeButtonUpSkin"));
        maximizeButton.setStyle("disabledSkin", getStyle("maximizeButtonDisabledSkin"));
        maximizeButton.setStyle("downSkin", getStyle("maximizeButtonDownSkin"));
        maximizeButton.setStyle("overSkin", getStyle("maximizeButtonOverSkin"));

        minimizeButton.setStyle("upSkin", getStyle("minimizeButtonUpSkin"));
        minimizeButton.setStyle("disabledSkin", getStyle("minimizeButtonDisabledSkin"));
        minimizeButton.setStyle("downSkin", getStyle("minimizeButtonDownSkin"));
        minimizeButton.setStyle("overSkin", getStyle("minimizeButtonOverSkin"));

        dispatchEvent(new Event("restore"));
    }

    /**
     *  @private
     */
    private function toPseudoMax():void
    {
        if(currentState == "maximized")
            pushStateProperties(statePseudoMax, x, y,
                                width, height,
                                NaN, NaN);
    }

}
}
